#include <kieper/AbstractRpcChannel.hpp>
#include <kieper/PacketHeader.hpp>

namespace kieper {
    using ::google::protobuf::Closure;
    using ::google::protobuf::Message;
    using ::google::protobuf::MethodDescriptor;
    using ::google::protobuf::RpcController;

    AbstractRpcChannel::AbstractRpcChannel() {}
    AbstractRpcChannel::~AbstractRpcChannel() {}
    
    void AbstractRpcChannel::CallMethod(const MethodDescriptor* method, 
      RpcController* controller, const Message* request, Message* response,
      Closure* done) {
        unsigned char* sendBuffer = getSendBuffer();

        PacketHeader header = packetAssembler_.makePacketHeader(request,
          static_cast<uint16_t>(method->index()));
        
        size_t packetSize = packetAssembler_.makePacket(sendBuffer,
          &header, request);
        uint32_t packetId = header.packetId();

        callbacks_[packetId] = done;
        responses_[packetId] = response;

        doSend(sendBuffer, packetSize);
    }

    void AbstractRpcChannel::onPacketReceived(unsigned char* packetBuf, size_t packetLen) {
        PacketHeader header(packetBuf);

        uint32_t packetId = header.packetId();

        Callbacks::iterator clIt = callbacks_.find(packetId);
        if (clIt == callbacks_.end()) {
            // Received response for which there is no request
            // TODO: log warning
            return;
        }
        
        Closure* replyReadyClosure = clIt->second;
        callbacks_.erase(clIt);

        Message* reply = responses_[packetId];
        responses_.erase(packetId);
    
        unsigned char* messageBuf = packetBuf + PacketHeader::HEADER_SIZE;

        size_t messageLen = packetLen - PacketHeader::HEADER_SIZE;
        reply->ParseFromArray(messageBuf, messageLen);
        replyReadyClosure->Run();
    }

    boost::asio::io_service& AbstractRpcChannel::getIoService() {
        return ios_;
    }

    unsigned char* AbstractRpcChannel::getSendBuffer() {
        return sendBuffer_;
    }
    
    unsigned char* AbstractRpcChannel::getRecvBuffer() {
        return recvBuffer_;
    }

}
